#pragma once

template <typename E>
class LoopQueue {
public:
    LoopQueue() :LoopQueue(10) {} 
    LoopQueue(int capacity)
        : _size(0),
        _front(0),
        _tail(0),
        _capacity(capacity),
        _data(new E[capacity + 1])
    {
    }

    ~LoopQueue() = default;

    int size() 
    {
        return _size;
    }

    int capacity()
    {
        return _capacity;
    }
    bool empty()  
    {
        return _front == _tail;
    }

    void enqueue(E e)  
    {
        if ((_tail + 1) % (_capacity + 1) == _front) 
        {
            resize(_capacity * 2);
        }
        _data[_tail] = e;
        _tail = (_tail + 1) % (_capacity + 1);
        _size++;
    }

    void resize(int newCapacity)
    {
        _capacity = newCapacity;
        E *newData = new E[newCapacity + 1];
        for (int i = 0; i < _size; ++i)
            newData[i] = _data[(i + _front) % (_capacity + 1)];
        delete _data;
        _data = newData;  
        _front = 0;
        _tail = _size;
    }

    E dequeue() 
    {
        if (empty())
            throw runtime_error("Cannot dequeue from an empty queue");
        E temp = _data[_front];
        _data[_front] = 0;
        _front = (_front + 1) % (_capacity + 1);
        _size--;
        if (_size == _capacity / 4 && _capacity / 2 != 0) {
            resize(_capacity / 2);
        }
        return temp;
    }

    E front()
    {
        if (empty()) {
            throw runtime_error("Cannot dequeue from an empty queue");
        }
        return _data[_front];
    }

private:
    E *_data;
    int _front, _tail;
    int _size, _capacity;
};
